package com.study.sequence.common.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.study.sequence.common.CommonConstract;
import com.study.sequence.common.FlushService;
import com.study.sequence.common.SequenceBuffer;
import com.study.sequence.common.SequenceDTO;
import com.study.sequence.db.entity.BtSequence;
import com.study.sequence.db.service.IBtSequenceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by 张波 on 2020/5/23.
 */
@Slf4j
@Component
public class FlushServiceImpl implements FlushService {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    @Autowired
    private IBtSequenceService btSequenceService;

    /**
     * 初始化sequence
     *
     * @param businessKey
     * @return
     */
    @Override
    public SequenceDTO init(String businessKey) {
        BtSequence btSequence = new BtSequence();
        businessKey = StringUtils.isBlank(businessKey) ? CommonConstract.DEFAULT_BUSINESS_KEY : businessKey;
        btSequence.setBusinessKey(businessKey);
        btSequence.setDelInd(CommonConstract.DEL_IND);
        btSequence = btSequenceService.getOne(new QueryWrapper<>(btSequence));
        if (null == btSequence) {
            /*序列不存在，创建新默认序列*/
            btSequence = createDefaultSequence(businessKey);
            btSequenceService.save(btSequence);
        }
        /*构建sequeceDTO对象，并检验是否进行初始化*/
        SequenceDTO sequenceDTO = new SequenceDTO(businessKey, this);
        if (!sequenceDTO.isInit()) {
            sequenceDTO.init(btSequence.getValueSize(), flush(businessKey), btSequence.getStepValue());
        }
        return sequenceDTO;
    }

    /**
     * 构建默认sequence
     *
     * @return
     */
    private BtSequence createDefaultSequence(String businessKey) {
        BtSequence btSequence = new BtSequence();
        if (StringUtils.isBlank(businessKey)) {
            btSequence.setBusinessKey(CommonConstract.DEFAULT_BUSINESS_KEY);
        }
        btSequence.setBusinessKey(businessKey);
        btSequence.setValueSize(8);
        btSequence.setCurrentValue(0);
        btSequence.setStepValue(100);
        btSequence.setDelInd(CommonConstract.DEL_IND);
        btSequence.setCreateTime(LocalDateTime.now());
        btSequence.setUpdateTime(LocalDateTime.now());
        return btSequence;
    }

    /**
     * 刷新sequence
     *
     * @param sequenceDTO
     * @return
     */
    @Override
    public boolean flush(SequenceDTO sequenceDTO) {
        try {
            log.info("进入缓存刷新逻辑");
            sequenceDTO.getFlushLock().lock();
            log.info("进入缓存刷新逻辑，成功获取到锁！");
            sequenceDTO.setFlushTime(LocalDateTime.now());
            SequenceBuffer sequenceBuffer = sequenceDTO.getBufferFlag().get() ? sequenceDTO.getSequenceBuffer_B() : sequenceDTO.getSequenceBuffer_A();
            if (sequenceBuffer.getFlush().get()) {
                /*如果已经刷新了，则不再进行操作*/
                return false;
            }
            if (sequenceBuffer.getIsInit().get() && !sequenceBuffer.isFlus(sequenceDTO.getProportion())) {
                /*检验是否达到了刷新buffer阈值*/
                return false;
            }
            sequenceBuffer.reset(this.flush(sequenceDTO.getBusinessKey()));
            /*如果刷新成功了，则更新刷新时间，刷新状态*/
            sequenceDTO.getIsFlus().compareAndSet(false, true);
            boolean flag = sequenceDTO.getBufferFlag().get();
            sequenceDTO.getBufferFlag().compareAndSet(flag, !flag);
//            log.info("序列对象刷新完成");
        } catch (Exception e) {
            log.error("序列刷新失败:{}", e);
        } finally {
            /*重置线程刷新状态，让后续调度请求再来进行刷新*/
            sequenceDTO.getThreadFlus().compareAndSet(true, false);
            sequenceDTO.getFlushLock().unlock();
            log.info("刷新完成，释放锁");
        }
        return true;
    }

    private Integer flush(String businessKey) {
        BtSequence btSequence = null;
        Integer result = null;
        do {
            btSequence = getBtSequence(businessKey);
            if (btSequence == null) {
                throw new RuntimeException("未能获取到sequence规则对象");
            }
            result = btSequence.getCurrentValue();
        } while (!updateBtSequence(btSequence));
        return result;
    }

    private BtSequence getBtSequence(String businessKey) {
        BtSequence btSequence = new BtSequence();
        btSequence.setBusinessKey(businessKey);
        btSequence.setDelInd(CommonConstract.DEL_IND);
        btSequence = btSequenceService.getOne(new QueryWrapper<>(btSequence));
        return btSequence;
    }

    private boolean updateBtSequence(BtSequence btSequence) {
        BtSequence where = new BtSequence();
        where.setId(btSequence.getId());
        where.setBusinessKey(btSequence.getBusinessKey());
        where.setCurrentValue(btSequence.getCurrentValue());
        btSequence.setCurrentValue(btSequence.getCurrentValue() + 1);
        btSequence.setUpdateTime(LocalDateTime.now());
        UpdateWrapper<BtSequence> wrapper = new UpdateWrapper<>(where);
        return btSequenceService.update(btSequence, wrapper);
    }
}
